package a5.grammar.productions;

import java.util.LinkedList;
import java.util.List;

import a5.common.ProgramCursor;
import a5.common.OrderedTree;
import a5.common.Token;
import a5.environment.Environment;
import a5.grammar.annotations.Grammar;
import a5.grammar.grammars.G1;
import a5.grammar.interfaces.Production;

/**
 * <pre>
 * This class provides a method for interpreting the following production
 * Program -> Expr | Local {Local} find Expr
 * 
 * Essentially, there are two paths of execution for this production.
 * 	a. Evaluate the {@link a5.grammar.productions.Expr Expr} or
 *  b. Begin a binding (or several) and invoke a {@link a5.function.Function Function}
 *  
 * This production uses recursive decent to interpret and parse.
 * </pre>
 * 
 * @author Jason Campos
 * @version Assignment 5, CS 152 (Programming Paradigms) Fall 2012, Jeffrey Smith
 */

@Grammar(clazz=G1.class)
public class Program implements Production{
	// Production dependencies
	private Expr expr;
	private Local local;
	
	// Hide constructor
	private Program (){}
	
	/**
	 * Interprets the program being examined by the argument interpreter 
	 * using the defined rules for this production. 
	 * 
	 * @param environment
	 * The {@link a5.environment.Environment Environment} responsible for variable and function maintenance
	 * and scoping. 
	 * 
	 * @param cursor
	 * The {@link a5.common.ProgramCursor ProgramCursor} which contains the {@link a5.common.Token Tokens} 
	 * of the program being interpreted.
	 * 
	 * @return
	 * The {@code String} result of the evaluated expression or function. 
	 */
	public String interpret(Environment environment, ProgramCursor cursor){
		// If the current point of execution points to an Expr, 
		// interpret and exit (that was easy).
		if(expr.first(cursor))
			return expr.interpret(environment, cursor);
		
		// Otherwise, begin building a function to invoke.
		// This process starts with Local.interpret() to bind a variable or 
		// function name to a key.
		local.interpret(environment, cursor);
		
		// More than one binding may exist. For example, several functions may be defined
		// or several variable names set. 
		while(local.first(cursor))
			local.interpret(environment, cursor);
		
		// Make sure the next token is "find"
		cursor.match(environment, "find");
		
		// Finally, interpret the expr
		return expr.interpret(environment, cursor);
	}
	
	/**
	 * Recursively parses the program of the argument cursor using the variable/function scoping
	 * policies of the argument environment.
	 * 
	 * @param cursor
	 * The {@link a5.common.ProgramCursor ProgramCursor} which contains the {@link a5.common.Token Tokens} 
	 * of the program being interpreted.
	 * 
	 * @return
	 * An {@link a5.common.OrderedTree OrderedTree<Token>} representing the parse structure of the 
	 * the program of the argument cursor.
	 */
	public OrderedTree<Token> parse(ProgramCursor cursor){
		List<OrderedTree<Token>> children = new LinkedList<OrderedTree<Token>>();
		
		// Expr.first() will determine whether or not
		// the first or second production is being applied
		if(expr.first(cursor)){
			// Program -> Expr
			children.add(expr.parse(cursor));
		} else {
			//Program -> Local {Local} find Expr
			children.add(local.parse(cursor));
			
			// Peek at the next token (Local.match() advanced the token). 
			// If Local.first() returns true, then add another
			// Local to the parse. This repeats until Local.first() 
			// returns false
			while(local.first(cursor))
				children.add(local.parse(cursor));
			
			children.add(new OrderedTree<Token>(new Token(cursor.match("find"))));
			children.add(expr.parse(cursor));
		}
		
		return new OrderedTree<Token>(new Token(this.getClass().getSimpleName()), children);
	}
	
	/**
	 * @param cursor
	 * The {@link a5.common.ProgramCursor ProgramCursor} which contains the {@link a5.common.Token Tokens} 
	 * of the program being interpreted.
	 * 
	 * @return
	 * {@code true} if the execution point of the program is at a point which indicates that
	 * an Program is a valid path of execution. In other words, if the current point of
	 * execution contains an element which is the first element of this production (Expr, Local).
	 */
	public boolean first(ProgramCursor cursor){
		return expr.first(cursor) || local.first(cursor);
	}
}
